package edu.csu.smartpark.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import com.obs.services.model.PutObjectResult;
import edu.csu.smartpark.dao.ArticleInfoDAO;
import edu.csu.smartpark.model.DO.ArticleDO;
import edu.csu.smartpark.model.DTO.ArticleDTO;
import edu.csu.smartpark.model.DTO.ArticleQueryDTO;
import edu.csu.smartpark.model.PO.ArticleInfo;
import edu.csu.smartpark.model.PO.ArticleInfoPO;
import edu.csu.smartpark.model.VO.ArticleVO;
import edu.csu.smartpark.model.VO.PageVO;
import edu.csu.smartpark.model.common.BusinessException;
import edu.csu.smartpark.service.ArticleService;
import edu.csu.smartpark.util.CommonUtil;
import edu.csu.smartpark.util.OssUtil;
import edu.csu.smartpark.util.SnowFlakeGenerateIdUtil;
import edu.csu.smartpark.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.util.*;

@Service
@Slf4j
public class ArticleServiceImpl implements ArticleService {
    @Autowired
    private ArticleInfoDAO articleInfoDAO;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private OssUtil ossUtil;

    @Override
    /*
    * @Description: 创建文章
    * @Author: LZY
    * @Date: 2021/5/1 20:48
    * @Params: [articleDO]
    * @Return: java.lang.String
    */
    public String createArticle(ArticleDO articleDO) {
        /*
            1. 生成文章ID
            2. 向mysql数据库中插入一条文章记录
            3. 向MongoDB中插入一条文章记录
        */
        SnowFlakeGenerateIdUtil snowFlakeGenerateIdUtil = new SnowFlakeGenerateIdUtil(5,5);
        String articleId = String.valueOf(snowFlakeGenerateIdUtil.nextId());
        Date currentTime = CommonUtil.getCurrentTime();
        ArticleInfoPO articleInfoPO = new ArticleInfoPO();
        BeanUtils.copyProperties(articleDO,articleInfoPO);
        articleInfoPO.setReleaseTime(currentTime);
        articleInfoPO.setIsDeleted(0);
        articleInfoPO.setArticleId(articleId);
        articleInfoPO.setCoverImageUrl(StringUtil.join(articleDO.getCoverImageUrl()));
        articleInfoDAO.insert(articleInfoPO);
        log.info("[createArticle] insert article record into mysql success");

        ArticleInfo articleInfo = new ArticleInfo();
        BeanUtils.copyProperties(articleDO, articleInfo);
        articleInfo.setArticleId(articleId);
        mongoTemplate.save(articleInfo, "articles");
        log.info("[createArticle] insert article info into mongodb success");
        return articleId;
    }

    @Override
    /*
    * @Description: 删除文章
    * @Author: LZY
    * @Date: 2021/5/1 21:15
    * @Params: [articleId]
    * @Return: int
    */
    public int deleteArticle(String articleId) {
        /*
            1. 删除obs图像数据
                a. 根据url转换为对象名
                b. 删除obs对象
            2. 删除MongoDB数据库中文章记录
            3. 删除mysql数据库中文章记录
         */
        Query query = new Query(Criteria.where("articleId").is(articleId));
        ArticleInfo articleInfo = mongoTemplate.findOne(query, ArticleInfo.class);
        if (articleInfo == null){
            log.info("article is not exist in mongodb");
            return -1;
        }

        List<String> imageUrls = articleInfo.getImageUrls();
        // 删除obs中的图像文件
        for (String url : imageUrls){
            String imageObjectName = ossUtil.getObjectNameByUrl(url);
            ossUtil.deleteObject(imageObjectName);
        }
        DeleteResult result = mongoTemplate.remove(query, ArticleInfo.class);
        if (result.getDeletedCount() <= 0){
            log.info("[mongodb] article {} delete fail",articleId);
            return -1;
        }
        log.info("[mongodb] article {} delete success", articleId);

        UpdateWrapper<ArticleInfoPO> wrapper = new UpdateWrapper<>();
        wrapper.eq("article_id",articleId).set("is_deleted", 1);
        int row = articleInfoDAO.update(null, wrapper);
        if (row <= 0){
            log.info("[mysql] article {} delete fail",articleId);
            return -1;
        }
        log.info("[mysql] article {} delete success",articleId);
        return row;
    }

    @Override
    public int updateArticleInfo(ArticleDO articleDO) {
        if (articleDO == null){
            log.info("[updateArticleInfo] article info is empty");
            return -1;
        }

        Query query = new Query(Criteria.where("articleId").is(articleDO.getArticleId()));
        // 只有这两个字段更新了，才需要更新obs中的图像数据
        if (articleDO.getCoverImageUrl() != null || articleDO.getImageUrls() != null){
            // 更新obs中的图像数据
            ArticleInfo oldArticleInfo = mongoTemplate.findOne(query, ArticleInfo.class);
            if (oldArticleInfo == null){
                log.info("article is not exist, can not be update");
                return -1;
            }
            QueryWrapper<ArticleInfoPO> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("article_id", articleDO.getArticleId());
            ArticleInfoPO oldArticleInfoPO = articleInfoDAO.selectOne(queryWrapper);
            if (oldArticleInfoPO == null){
                log.info("article is not exist, can not be update");
                return -1;
            }
            Set<String> oldImageUrls = new HashSet<>();
            if (oldArticleInfo.getImageUrls() != null){
                oldImageUrls.addAll(oldArticleInfo.getImageUrls());
            }
            if (oldArticleInfoPO.getCoverImageUrl() != null && !oldArticleInfoPO.getCoverImageUrl().equals("")){
                oldImageUrls.addAll(StringUtil.splits(oldArticleInfoPO.getCoverImageUrl()));
            }
            Set<String> newImageUrls = new HashSet<>();
            if (articleDO.getImageUrls() != null){
                newImageUrls.addAll(articleDO.getImageUrls());
            }
            if (articleDO.getCoverImageUrl() != null){
                newImageUrls.addAll(articleDO.getCoverImageUrl());
            }

            // 对比文章新旧图像urls，删除被丢弃的图像数据
            for (String url: oldImageUrls){
                if (!newImageUrls.contains(url)){
                    String imageObjectName = ossUtil.getObjectNameByUrl(url);
                    ossUtil.deleteObject(imageObjectName);
                }
            }
        }

        // 更新mysql文章属性相关字段数据
        UpdateWrapper<ArticleInfoPO> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("article_id", articleDO.getArticleId());
        String title = articleDO.getTitle();
        if (title != null){
            updateWrapper.set("title", title);
        }
        List<String> coverImageUrl = articleDO.getCoverImageUrl();
        if (coverImageUrl != null){
            updateWrapper.set("cover_image_url", coverImageUrl);
        }
        int row = articleInfoDAO.update(null, updateWrapper);
        if (row <= 0){
            log.info("[mysql] update article {} info fail",articleDO.getArticleId());
            return -1;
        }
        log.info("[mysql] update article {} info success",articleDO.getArticleId());

        // 更新MongoDB中字段数据
        Update update = new Update();
        String text = articleDO.getText();
        if (text != null){
            update.set("text", text);
        }
        List<String> imageUrls = articleDO.getImageUrls();
        if (imageUrls != null){
            update.set("imageUrls", imageUrls);
        }
        UpdateResult result = mongoTemplate.updateFirst(query, update,ArticleInfo.class);
        if (result == null){
            log.info("[mongodb] update article {} info fail",articleDO.getArticleId());
            return -1;
        }
        log.info("[mongodb] update article {} info success",articleDO.getArticleId());
        return row;
    }

    @Override
    /*
    * @Description: 查询文章列表，不包含文章具体信息
    * @Author: LZY
    * @Date: 2021/5/1 20:51
    * @Params: ArticleQueryDTO
    * @Return: PageVO<ArticleVO>
    */
    public PageVO<ArticleVO> queryArticle(ArticleQueryDTO queryDTO) throws BusinessException {
        if (queryDTO == null){
            throw new BusinessException(BusinessException.PARAMETERERROR, "article query dto is null");
        }
        QueryWrapper<ArticleInfoPO> wrapper = new QueryWrapper<>();
        if (queryDTO.getArticleType() != null && queryDTO.getArticleType().size() > 0){
            wrapper.in("type", queryDTO.getArticleType());
        }
        if (!StringUtil.isEmpty(queryDTO.getParkId())){
            wrapper.eq("park_id", queryDTO.getParkId());
        }
        Integer pageNum = queryDTO.getPageNum() == null ? 1 : queryDTO.getPageNum();
        Integer pageSize = queryDTO.getPageSize() == null ? 10 : queryDTO.getPageSize();
        Page<ArticleInfoPO> page = new Page<>(pageNum, pageSize);
        IPage<ArticleInfoPO> iPage = articleInfoDAO.selectPage(page, wrapper);
        List<ArticleVO> articleVOS  = new LinkedList<>();
        for (ArticleInfoPO article : iPage.getRecords()){
            ArticleVO articleVO = new ArticleVO();
            BeanUtils.copyProperties(article, articleVO);
            articleVO.setCoverImageUrl(StringUtil.splits(article.getCoverImageUrl()));
            articleVOS.add(articleVO);
        }
        PageVO<ArticleVO> res = new PageVO<>();
        res.setPageNum(iPage.getCurrent());
        res.setPageSize(iPage.getSize());
        res.setPages(iPage.getPages());
        res.setTotal(iPage.getTotal());
        res.setRecords(articleVOS);
        return res;
    }

    @Override
    /*
    * @Description: 从MongoDB中读取文章的详细信息
    * @Author: LZY
    * @Date: 2021/4/29 12:09
    * @Params: [articleId]
    * @Return: edu.csu.smartpark.model.DO.ArticleDO
    */
    public ArticleDO getArticleInfoByArticleId(String articleId) {
        Query query = new Query(Criteria.where("articleId").is(articleId));
        ArticleInfo articleInfo = mongoTemplate.findOne(query, ArticleInfo.class);
        if (articleInfo == null){
            log.info("article id is empty, find article {} fail", articleId);
            return null;
        }
        ArticleDO articleDO =  new ArticleDO();
        BeanUtils.copyProperties(articleInfo, articleDO);
        return articleDO;
    }

    /*
    * @Description: 创建文章时，上传多张图片到obs，将url返回给前端
    * @Author: LZY
    * @Date: 2021/4/29 10:54
    * @Params: 文章ID，图片数组;如果文章ID为空，则会自己生成一个ID并插入图片数据，反之则是根据文章ID更新图片数据。
    * @Return: 返回数据格式为map，key是图片的文件名，value是图片存储的URL
    */
    @Override
    public List<String> uploadArticleImages(MultipartFile[] files) {
        List<String> result = new LinkedList<>();
        for (MultipartFile file : files){
            PutObjectResult uploadImageResult = ossUtil.uploadImage(file);
            if (uploadImageResult == null){
                log.error("[smart park server] image: {} upload fail", file.getOriginalFilename());
                // 图片上传失败
                continue;
            }
            log.info("[smart park server] image: {} upload success", file.getOriginalFilename());
            result.add(uploadImageResult.getObjectUrl());
        }
        return result;
    }

}
